package cn.edu.fudan.dsm.MultiIndexes;

import cn.edu.fudan.dsm.MultiIndexes.Utils.RandomUtils;
import cn.edu.fudan.dsm.MultiIndexes.data.GaussianGenerator;
import cn.edu.fudan.dsm.MultiIndexes.data.RandomWalkGenerator;
import cn.edu.fudan.dsm.MultiIndexes.data.SeriesGenerator;
import cn.edu.fudan.dsm.MultiIndexes.data.SineGenerator;
import org.apache.commons.io.FileUtils;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class DataGeneratorForNorm {

    private static final String DATA_FILENAME_PREFIX = "files" + File.separator + "data" + File.separator;

    private List<SeriesGenerator> generators = new ArrayList<>();
    private long dataLength;

    public DataGeneratorForNorm(long dataLength) throws IOException {
        this.dataLength = dataLength;
    }

    public static void main(String args[]) throws IOException {
        System.out.print("Data Length = ");
        Scanner scanner = new Scanner(System.in);
        long dataLength = scanner.nextLong();
        System.out.print("Generate data? [true/false] = ");
        boolean generateData = scanner.nextBoolean();

        scanner.close();

        DataGeneratorForNorm generator = new DataGeneratorForNorm(dataLength);
        if (generateData) {
            generator.generateSyntheticDataToFile();
        }
    }

    private double[] generateSegment(int maxLength) {
        int t = RandomUtils.random(0, generators.size() - 1);
        int l = RandomUtils.random(Math.min(1000, maxLength), maxLength);
        SeriesGenerator seriesGenerator = generators.get(t);
        return seriesGenerator.generate(l);
    }

    private void generateSyntheticDataToFile() {
        double ex = 0.0;
        double ex2 = 0.0;
        int len = 0;
        float[] ts = new float[1000000];
        RandomWalkGenerator randomWalkGenerator = new RandomWalkGenerator(-5, 5, 0, 1);
        generators.add(randomWalkGenerator);
        GaussianGenerator gaussianGenerator = new GaussianGenerator(-5, 5, 0, 2);
        generators.add(gaussianGenerator);
        SineGenerator sineGenerator = new SineGenerator(2, 10, 2, 10, -5, 5);
        generators.add(sineGenerator);

        File file = new File(DATA_FILENAME_PREFIX + dataLength + "l");
        try {
            FileUtils.forceMkdirParent(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try (FileOutputStream fos = new FileOutputStream(file)) {
            DataOutputStream dos = new DataOutputStream(fos);
            long left = dataLength;
            int count = 0;
            while (left > 0) {
                // generate the following segment by the generator
                double[] segment = generateSegment((int)Math.min(left, dataLength / 100000));

                // write the segment to file
                for (double data : segment) {
                    ts[len] = (float)data;
                    ex += (float)data;
                    ex2 += ((float)data) * ((float)data);
                    len ++;
                    if(len == (dataLength / 100000))break;
                    //dos.writeFloat((float) data);
                }
                if(len == (dataLength / 100000)){
                    System.out.println("count : " + count);
                    count ++;
                    ex /= len;
                    ex2 = Math.sqrt(ex2 / len - ex*ex);
                    System.out.println("len " + len);
                    for(int j = 0;j < len; j++){
                        ts[j] -= ex;
                        ts[j] /= ex2;
                        dos.writeFloat(ts[j]);
                    }
                    ex = 0;
                    ex2 = 0;
                    left -= len;
                    len = 0;
                }
                System.out.println("left " + left);
            }
            dos.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
